home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / pascal / totdoc.zip / CHAPT20.TXT < prev    next >
Text File  |  1991-02-11  |  35KB  |  799 lines

  1.                                                                        Extending
  2.                                                                            Input
  3.                                                                            Field
  4.                                                                            Types
  5.  
  6.  
  7.  
  8.          "Stay humble. Always answer the phone - no matter who else is in the
  9.          car."
  10.  
  11.                                                                      Jack Lemmon
  12.  
  13.  
  14.          One of the most used elements of the Toolkit is the full screen input
  15.          facility. If the Toolkit field types do not meet your exact needs, you
  16.          can create your own custom field types. This chapter explains how.
  17.  
  18.  
  19. The Input Object Hierarchy
  20.          The objects FormOBJ and WinFormOBJ are used to manage and control full
  21.          screen input. You may recall that the method AddItem is used to add
  22.          individual input fields to the form. AddItem accepts any of the input
  23.          fields shown in the TOTIO Object Hierarchy on page 11.5. As the diagram
  24.          illustrates, all IO objects are descended from the root object Base-
  25.          IOOBJ. If you want to create new input field objects which can be
  26.          managed by the form objects, the new objects must be descended from
  27.          BaseIOOBJ, or any object descended from BaseIOOBJ.
  28.  
  29.          The BaseIOOBJ object includes the following data and methods, which are
  30.          inherited by all descendant objects:
  31.          ItemIOOBJ = object
  32.             vBoundary: tCoords;
  33.             vHotKey: word;
  34.             vID: word;
  35.             vActive: boolean;
  36.             {methods ...}
  37.             constructor Init;
  38.             procedure   SetActiveStatus(Selectable:boolean);
  39.             function    Active:boolean;
  40.             function    GetHotKey: word;
  41.             procedure   SetHotkey(HK:word);
  42.             function    GetID: word;
  43.             procedure   SetID(ID:word);
  44.             function    Visible: boolean;                        VIRTUAL;
  45.             procedure   Display(Status:tStatus);                 VIRTUAL;
  46.             function    Select(K:word; X,Y:byte):tAction;        VIRTUAL;
  47.             function    ProcessKey(InKey:word;X,Y:byte):tAction; VIRTUAL;
  48.             function    Suspend:boolean;                         VIRTUAL;
  49.             destructor  Done;                                    VIRTUAL;
  50.          end; {ItemIOOBJ}
  51.  
  52.          Note: the BaseIOOBJ also includes signal-related methods. These are
  53.          discussed in a later section.
  54.  
  55. 20-2                                                       Extending the Toolkit
  56. --------------------------------------------------------------------------------
  57.  
  58.          The vBoundary variable identifies the (X1,Y1) and (X2,Y2) coordinates
  59.          of the field. When the user clicks the left mouse button during full-
  60.          form input, the form object scans the list of active input objects and
  61.          moves the user to the input object with coordinates corresponding to
  62.          the mouse cursor position. Any descendant field should therefore update
  63.          the vBoundary variable to indicate the physical location of the field.
  64.          The other three BaseIOOBJ variables identify the field's hotkey, ID and
  65.          whether the field is active or selectable. These variables are managed
  66.          by the BaseIOOBJ methods SetActiveStatus, Active, GetHotkey, SetHotkey,
  67.          SetID and GetID. All these methods are suitable for any field type, and
  68.          should not need modification in descendant objects. Just inherit them
  69.          and use them!
  70.  
  71.          The virtual methods, highlighted in bold, are specific to each descen-
  72.          dant object. As a minimum, any descendant objects should redefine these
  73.          bold methods -- they are the main methods called by the form object
  74.          during full-screen input.
  75.          Apart from special hotkeys and navigation control keys, all the user
  76.          input fields are visible. That is, the user can see them. As the TOTIO
  77.          Hierarchy Diagram illustrates, all visible fields are descended from
  78.          VisibleIOOBJ, which is, in turn, descended from BaseIOOBJ. In addition
  79.          to the BaseIOOBJ objects just discussed, the VisibleIOOBJ objects
  80.          inherit the following methods:
  81.  
  82.            procedure   SetLabel(Lbl:string);
  83.            procedure   SetMessage(X,Y:byte; Msg:string);
  84.            procedure   WriteMessage;
  85.            procedure   WriteLabel(Status:tStatus);               VIRTUAL;
  86.          As their names suggest, these methods are used to set and display
  87.          labels and messages. Labels are displayed to the immediate left of a
  88.          field and act as a field title. A message is the field's descriptive
  89.          text which is displayed when the user moves to the field. Under normal
  90.          circumstances you will not need to modify these methods. They are
  91.          appropriate to any field type.
  92.  
  93.  
  94.  
  95. Creating New Field Types
  96.          When you want to create a new field object, you must decide which
  97.          existing field object has the properties most closely resembling the
  98.          new field type you want to create. For example, if the field includes
  99.          data input, then you would probably create a descendant of CharIOOBJ.
  100.          However, if the field has multiple lines (like a radio button or list),
  101.          then the new object would best be a descendant of MultiLineIOOBJ. If
  102.          none of the existing fields come anywhere close, create a descendant
  103.          from VisibleIOOBJ.
  104.  
  105.  
  106.  
  107. Extending Input Field Types                                                 20-3
  108. --------------------------------------------------------------------------------
  109.  
  110.          To illustrate the principles, a new boolean object will be created.
  111.          This object will display two different options, e.g. Yes or No, True or
  112.          False, Live or Die, etc. The field will display one of the options, and
  113.          when the user presses the space bar or clicks the mouse, the field will
  114.          flip to the other option.
  115.  
  116.          Since the boolean object does not process individual character input,
  117.          and does not occupy multiple lines, the best object to descend from is
  118.          VisibleIOOBJ. The following methods are inherited from VisibleIOOBJ and
  119.          do not need to be modified:
  120.             procedure   SetActiveStatus(Selectable:boolean);
  121.             function    Active:boolean;
  122.             function    GetHotKey: word;
  123.             procedure   SetHotkey(HK:word);
  124.             function    GetID: word;
  125.             procedure   SetID(ID:word);
  126.             procedure   SetLabel(Lbl:string);
  127.             procedure   SetMessage(X,Y:byte; Msg:string);
  128.             procedure   WriteMessage;
  129.             procedure   WriteLabel(Status:tStatus);              VIRTUAL;
  130.             function    Visible: boolean;                        VIRTUAL;
  131.  
  132.          As well as replacing Init and Done, the primary inherited methods which
  133.          need to be over-written are Display, Select, Processkey and Suspend.
  134.          These four methods are called by the form object during full screen
  135.          input. Additionally, if you want the new boolean object to function
  136.          stand-alone, i.e. without being part of a form, an Activate method
  137.          should be added. Activate will display the field and process user input
  138.          until [KEYCAP] or [KEYCAP] is pressed.
  139.          In keeping with the Toolkit style, SetValue and GetValue methods should
  140.          also be added. These methods are used to set the object's default
  141.          value, i.e. which option to display when the field is activated, and to
  142.          get the user-selected value after input is complete.
  143.  
  144.          The new boolean object will need to include three data variables: the
  145.          two strings that represent the true and false settings, and a boolean
  146.          to record the object's actual value.
  147.          After all the methods and data have been included, the definition of
  148.          the new BooleanIOOBJ is as follows:
  149.  
  150.          BooleanIOOBJ = object (VisibleIOOBJ)
  151.             OnString: StringBut;
  152.             OffString: StringBut;
  153.             vInput: boolean;
  154.             {methods...}
  155.             Constructor Init(X,Y:byte; Yes,No:stringbut);
  156.             function    GetValue: boolean;
  157.             procedure   SetValue(On:boolean);
  158.             procedure   Activate;
  159.  
  160.  
  161.  
  162. 20-4                                                       Extending the Toolkit
  163. --------------------------------------------------------------------------------
  164.  
  165.             procedure   Display(Status:tStatus);                  VIRTUAL;
  166.             function    Select(K:word; X,Y:byte):tAction;         VIRTUAL;
  167.             function    ProcessKey(InKey:word;X,Y:byte):tAction;  VIRTUAL;
  168.             function    Suspend:boolean;                          VIRTUAL;
  169.             destructor  Done;                                     VIRTUAL;
  170.          end; {BooleanIOOBJ}
  171.  
  172.  
  173.          In the following sections, each method is individually discussed:
  174.  
  175.  
  176.  
  177. Extending Input Field Types                                                 20-5
  178. --------------------------------------------------------------------------------
  179.  
  180. Init
  181.  
  182.          The primary responsibilities of the Init method are to set the values
  183.          of the true and false strings, and to update the vBoundary variable
  184.          with the location of the field. In keeping with the other input fields,
  185.          the Init method is passed the (X,Y) coordinate of the leftmost charac-
  186.          ter. By finding the length of the longest string, the method can com-
  187.          pute the rightmost (X,Y) coordinate. The method detail is, therefore,
  188.          as follows:
  189.          constructor BooleanIOOBJ.Init(X,Y:byte; Yes,No:stringbut);
  190.          {}
  191.          var L:byte;
  192.          begin
  193.             VisibleIOOBJ.Init;
  194.             OnString := Yes;
  195.             OffString := No;
  196.             L := length(OnString);
  197.             if L < length(OffString) then
  198.                L := length(OffString);
  199.             with vBoundary do
  200.             begin
  201.                X1 := X;
  202.                X2 := X + pred(L);
  203.                Y1 := Y;
  204.                Y2 := Y;
  205.             end;
  206.             vInput := true;
  207.          end; {BooleanIOOBJ.Init}
  208.  
  209.  
  210.  
  211. SetValue and GetValue
  212.          These methods are short and to the point. All they do is set or return
  213.          the value of the field, and are defined as follows:
  214.  
  215.          function BooleanIOOBJ.GetValue: boolean;
  216.          {}
  217.          begin
  218.             GetValue := vInput;
  219.          end; {BooleanIOOBJ.GetValue;
  220.  
  221.          procedure BooleanIOOBJ.SetValue(On:boolean);
  222.          {}
  223.          begin
  224.             vInput := On;
  225.          end; {BooleanIOOBJ.SetValue}
  226.  
  227.  
  228.  
  229.  
  230. 20-6                                                       Extending the Toolkit
  231. --------------------------------------------------------------------------------
  232.  
  233. Display
  234.  
  235.          Display is a virtual method which must be declared with a single passed
  236.          parameter of type tStatus. tStatus is an enumerated type with the mem-
  237.          bers HiStatus, Norm and Off, and the value is used to indicate whether
  238.          the field is highlighted (the field the user is currently editing),
  239.          normal (one of the other fields in a form), or inactive (cannot be
  240.          selected).
  241.          The primary responsibility of Display is to display the field contents
  242.          in the appropriate color. The first task is to decide the display
  243.          attribute. To be consistent with the other input objects, the field
  244.          should ascertain the attribute by calling a TOTIO^ function method.
  245.          TOTIO controls the colors for field labels and messages, button fields,
  246.          group fields, list fields, and single line fields. In this case, the
  247.          single line field colors are appropriate. Refer to page 11-40 for a
  248.          full discussion of IOTOT.
  249.  
  250.          The BooleanIOOBJ method Display is implemented as follows:
  251.          procedure BooleanIOOBJ.Display(Status:tStatus);
  252.          {}
  253.          var Att: byte;
  254.          begin
  255.             case Status of
  256.                HiStatus: Att := IOTOT^.FieldCol(2);
  257.                Norm:     Att := IOTOT^.FieldCol(1);
  258.                Off:      Att := IOTOT^.FieldCol(4);
  259.             end; {case}
  260.             with vBoundary do
  261.                if vInput then
  262.                   Screen.WriteAT(X1,Y1,Att,padleft(OnString,succ(X2-X1),' '))
  263.                else
  264.                   Screen.WriteAT(X1,Y1,Att,padleft(OffString,succ(X2-X1),' '));
  265.          end; {BooleanIOOBJ.Display}
  266.  
  267.  
  268.  
  269. Select
  270.          The Select method is called by the form object whenever the user tries
  271.          to enter the field. The method is responsible for displaying the field
  272.          contents as well as the field's label and message. Select is also
  273.          responsible for moving the cursor to the field.
  274.  
  275.          Select is actually a function method which returns a member of the
  276.          enumerated type tAction. tAction is used to instruct the form object on
  277.          how to proceed, and includes the members None, NextField, PrevField,
  278.          Finished, Escaped, Refresh, Signal, Enter, Help, Stop1..Stop9. Under
  279.          normal circumstances, Select should return a value of None. This
  280.  
  281.  
  282.  
  283.  
  284. Extending Input Field Types                                                 20-7
  285. --------------------------------------------------------------------------------
  286.  
  287.          instructs the Toolkit to proceed as normal. The majority of the other
  288.          members are used by "buttons", which the user selects to finish the
  289.          input session or to ask for help.
  290.  
  291.          The BooleanIOOBJ method Select is implemented as follows:
  292.          function BooleanIOOBJ.Select(K:word; X,Y:byte):tAction;
  293.          {}
  294.          begin
  295.             Display(HiStatus);
  296.             WriteLabel(HiStatus);
  297.             WriteMessage;
  298.             Screen.GotoXY(vBoundary.X1,vBoundary.Y1);
  299.             Select := none;
  300.          end; {BooleanIOOBJ.Select}
  301.  
  302.  
  303.  
  304. ProcessKey
  305.          When a field is active, the form object repeatedly passes the user
  306.          input to the highlighted object. This continues until the user moves to
  307.          another field, or presses a key which indicates the user wants to fin-
  308.          ish the input session.
  309.  
  310.          The form object calls the field's method ProcessKey and passes the user
  311.          input to it. The ProcessKey method then updates the value of the field
  312.          based on the user's input. In the case of the BooleanIOOBJ field, the
  313.          field will flip to the other string whenever the keys [KEYCAP] [KEYCAP]
  314.          or [KEYCAP] are pressed. The field will also flip if the mouse is
  315.          clicked on the field. The Toolkit responds extremely quickly to a mouse
  316.          press, and it is a good idea to delay for a tenth of a second when the
  317.          mouse has been clicked. This overcomes the problem of the field flip-
  318.          ping a dozen or more times each time the user clicks the mouse.
  319.          ProcessKey is a function method which returns a member of the enumer-
  320.          ated type tAction. Under normal circumstances, the function should
  321.          return a value of None, which indicates that the form object should
  322.          continue passing keys to the field.
  323.  
  324.  
  325.  
  326.            Note: if you were creating a different input field type, you might
  327.            want to return a value of NextField when the current field became
  328.            full. This instructs the form object to suspend the current field
  329.            and select the next field.
  330.  
  331.  
  332.  
  333.          The BooleanIOOBJ method ProcessKey is implemented as follows:
  334.  
  335.  
  336.  
  337.  
  338. 20-8                                                       Extending the Toolkit
  339. --------------------------------------------------------------------------------
  340.  
  341.          function BooleanIOOBJ.ProcessKey(InKey:word;X,Y:byte):tAction;
  342.          {}
  343.          begin
  344.             if (InKey = 513)
  345.             or (InKey = 32)
  346.             or (inKey = 328)
  347.             or (InKey = 336) then
  348.             begin
  349.                vInput := not vInput;
  350.                Display(HiStatus);
  351.             end;
  352.             if InKey = 513 then {absorb mouse}
  353.                delay(100);
  354.             ProcessKey := None;
  355.          end; {BooleanIOOBJ.ProcessKey}
  356.  
  357.  
  358.  
  359. Suspend
  360.          The Suspend method is called by the form object when the user wants to
  361.          terminate input or move to another field. Suspend is responsible for
  362.          displaying the field and label in the normal attribute, and for remov-
  363.          ing the field message. All these tasks are performed by the inherited
  364.          VisibleIOOBJ method Suspend.
  365.  
  366.          Suspend is actually a function method which returns a boolean value.
  367.          This provides a mechanism for not allowing the user to leave the field.
  368.          If Suspend returns False, the form object stays in the field. This
  369.          facility should only be used when the user has entered some invalid
  370.          input, and it is good practice to display a message stating how the
  371.          user can correct the problem.
  372.          A user cannot enter an invalid value in a BooleanIOOBJ, and so Suspend
  373.          will always return True. The method Suspend is implemented as follows:
  374.  
  375.          function BooleanIOOBJ.Suspend:boolean;
  376.          {}
  377.          begin
  378.             Suspend := VisibleIOOBJ.Suspend;
  379.          end; {BooleanIOOBJ.Suspend}
  380.  
  381.  
  382. Activate
  383.  
  384.          The Activate method provides a way to use the object as a stand-alone
  385.          field, i.e. not as part of a form. Activate is responsible for select-
  386.          ing the field, getting input, and passing the input to ProcessKey.
  387.          Activate should repeatedly pass input to ProcessKey until the user
  388.          presses [KEYCAP] or [KEYCAP] to indicate the end of input. The method
  389.          Suspend should then be called to deselect the field.
  390.  
  391.  
  392.  
  393. Extending Input Field Types                                                 20-9
  394. --------------------------------------------------------------------------------
  395.  
  396.          The BooleanIOOBJ method Activate is implemented as follows:
  397.  
  398.          procedure BooleanIOOBJ.Activate;
  399.          {}
  400.          var
  401.             Action: tAction;
  402.          begin
  403.             Action := Select(0,0,0);
  404.             with Key do
  405.             begin
  406.                repeat
  407.                   GetInput;
  408.                   Action := ProcessKey(LastKey,LastX,LastY);
  409.                until ((LastKey = 324) or (LastKey = 13)) and Suspend;
  410.             end;
  411.          end; {BooleanIOOBJ.Activate}
  412.  
  413.  
  414.  
  415. Done
  416.  
  417.          Since BooleanIOOBJ has no dynamic data of its own, all Done needs to do
  418.          is call VisibleIOOBJ's method Done, as follows:
  419.          destructor BooleanIOOBJ.Done;
  420.          {}
  421.          begin
  422.             VisibleIOOBJ.Done;
  423.          end; {BooleanIOOBJ.Done}
  424.  
  425.  
  426.  
  427.  
  428.          That's the new BooleanIOOBJ defined. The full source code is contained
  429.          in the file EXTIO.PAS.
  430.  
  431.  
  432. Using BooleanIOOBJ
  433.  
  434.          Since BooleanIOOBJ is inherited from BaseIOOBJ, it can be used in full
  435.          form input just like any other input object. Listed below is the demo
  436.          program EXTDEM7.PAS which shows the new object in action. This demo
  437.          program is actually an adaptation of DEMIO2.PAS discussed in chapter
  438.          11. Figure 20.1 illustrates the generated display.
  439.          Program ExtendedDemoSeven;
  440.  
  441.          Uses DOS,CRT,
  442.               totFAST, totIO1, totIO2, extIO;
  443.  
  444.  
  445.  
  446.  
  447. 20-10                                                      Extending the Toolkit
  448. --------------------------------------------------------------------------------
  449.  
  450.          Var
  451.             Name: LateralIOOBJ;
  452.             Phone: PictureIOOBJ;
  453.             Price: FixedRealIOOBJ;
  454.             Status: BooleanIOOBJ;
  455.             Keys: ControlkeysIOOBJ;
  456.             Manager: FormOBJ;
  457.             Result: tAction;
  458.  
  459.          procedure InitVars;
  460.          {}
  461.          begin
  462.             with Name do
  463.             begin
  464.                Init(35,5,20,40);
  465.                SetLabel('Vendor Name');
  466.             end;
  467.             with Phone do
  468.             begin
  469.                Init(35,7,'(###) ###-####');
  470.                SetLabel('Tel');
  471.                SetRules(JumpIfFull);
  472.             end;
  473.             with Price do
  474.             begin
  475.                Init(35,9,8,2);
  476.                SetLabel('Unit Price');
  477.                SetValue(250.0);
  478.                SetMinMax(0.1,12250.0);
  479.                SetRules(EraseDefault);
  480.             end;
  481.             with Status do
  482.             begin
  483.                Init(35,11,' Nice Guy ',' Jerk ');
  484.                SetLabel('Category');
  485.             end;
  486.             Keys.Init;
  487.          end; {InitVars}
  488.          begin
  489.             ClrScr;
  490.             Screen.TitledBox(15,3,65,13,76,79,78,2,' Quicky Input Demo ');
  491.             Screen.WriteCenter(25,white,'Press TAB to switch fields
  492.                                          and press ESC or F10 to end');
  493.             InitVars;
  494.             with Manager do
  495.             begin
  496.                Init;
  497.                AddItem(Keys);
  498.  
  499.  
  500.  
  501.  
  502. Extending Input Field Types                                                20-11
  503. --------------------------------------------------------------------------------
  504.  
  505.                AddItem(Name);
  506.                AddItem(Phone);
  507.                AddItem(Price);
  508.                AddItem(Status);
  509.                Result := Go;
  510.                if Result = Finished then
  511.                   {update the database..}
  512.                else
  513.                   {call Esc routine};
  514.             end;
  515.          end.
  516.  
  517.  
  518.  
  519. Figure 20.1                                                             [SCREEN]
  520. Using
  521. BooleanIOOBJ
  522.  
  523.  
  524.  
  525. Understanding Signals
  526.          In sophisticated input forms, the data input by a user in one field may
  527.          affect the data of some related fields on the form. Signals provide
  528.          this capability in the Toolkit.
  529.  
  530.  
  531.  
  532. Signal Theory
  533.          The BaseIOOBJ object includes the following three signal-related meth-
  534.          ods:
  535.  
  536.          procedure   RaiseSignal(var TheSig:tSignal);                       VIR-
  537.          TUAL;
  538.          procedure   ShutdownSignal(var BaseSig:tSignal);
  539.          VIRTUAL;
  540.          procedure   HandleSignal(var BaseSig:tSignal; var NewSig:tSignal); VIR-
  541.          TUAL;
  542.          The totIO1 unit includes the record tSignal, which is defined as fol-
  543.          lows:
  544.  
  545.          tSignal = record
  546.             ID: word;
  547.             MsgType: word;
  548.             case word of           {variant record}
  549.             0: (MsgPtr: pointer);
  550.             1: (MsgLong: longint);
  551.             2: (MsgWord: word);
  552.             3: (MsgInt: integer);
  553.  
  554.  
  555.  
  556.  
  557. 20-12                                                      Extending the Toolkit
  558. --------------------------------------------------------------------------------
  559.  
  560.             4: (MsgByte: byte);
  561.             5: (MsgChar: char);
  562.          end;
  563.  
  564.          tSignal is a variant record which can be used to store any data which
  565.          needs to be communicated between input fields.
  566.          An input field's object methods Select, ProcessKey, and ProcessEnter
  567.          are function methods which return a value of type tAction. If any of
  568.          these methods return a value of SIGNAL, the form object will immedi-
  569.          ately call that input object's RaiseSignal method. This method is
  570.          passed a variable parameter of type tSignal. The variable is updated
  571.          with the information which needs to be passed to other fields. Each
  572.          signal has an ID, and the ID should be assigned a non-zero value. In a
  573.          situation where more than one signal can be raised, this ID will indi-
  574.          cate to the other fields which signal is being raised. The signal's
  575.          MsgType field can be used to provide further information about the
  576.          signal, and usually indicates the format of the data being passed with
  577.          the signal, e.g. a 1 might indicate a longint, a 2 might indicate a
  578.          word, etc.
  579.  
  580.  
  581.  
  582.            Note: Input objects which are descended from CharIOOBJ inherit the
  583.            virtual function method ProcessEnter. This method is passed no
  584.            parameters, and returns a value of type tAction. The method is
  585.            called whenever the user presses [KEYCAP]. It is typically used to
  586.            raise a signal or move the user to the next field.
  587.  
  588.  
  589.  
  590.          When a field raises a signal, the form manager passes the signal to the
  591.          next field in the form. This is achieved by calling the next field's
  592.          method HandleSignal. This method is passed the tSignal variable raised
  593.          by the originating field. The HandleSignal method can inspect the sig-
  594.          nal ID and, if appropriate, respond to the signal. If the field han-
  595.          dling the signal knows that the signal is not intended for any other
  596.          field, it can update the signal ID with a value of 0. This tells the
  597.          form object that the signal has been handled, and the signal is dis-
  598.          carded. Otherwise, the signal is passed to each input field in turn
  599.          until one of the fields sets the ID to 0, or until all the fields have
  600.          been passed the signal.
  601.  
  602.          When the signal has been handled or passed to every other field, the
  603.          originating field's ShutdownSignal method is called. This method can be
  604.          used to dispose of any data that was created specifically for the sig-
  605.          nal, and for any other housekeeping.
  606.  
  607.  
  608.  
  609.  
  610. Extending Input Field Types                                                20-13
  611. --------------------------------------------------------------------------------
  612.  
  613.          Any object which handles a signal can optionally raise a signal of its
  614.          own. The HandleSignal method is passed two parameters of type tSignal.
  615.          The first parameter is the original signal raised by another field. The
  616.          second parameter is an empty signal which the handling field can update
  617.          with its own signal. The form object inspects the second signal
  618.          returned by the field's HandleSignal method, and if the ID is set to a
  619.          non-zero value, a new signal is raised and passed to the other fields.
  620.          Only when this new signal has been handled will the form manager con-
  621.          tinue with the processing of the original signal.
  622.  
  623.  
  624.  
  625. A Signal Example
  626.          The way to use signals is best illustrated by example. In this section
  627.          a demo program will be developed which prompts the user to input some
  628.          directories, as a precursor to installing some software. The user is to
  629.          be prompted to input five different directories, one for the programs,
  630.          one for the doc files, etc. Like Turbo Pascal's own Install program,
  631.          each of the input fields needs to be updated if the user enters a new
  632.          default directory into the first field.
  633.  
  634.          To solve this problem, two new field objects must be created, and both
  635.          of them will be descended from StringIOOBJ. One object will be used to
  636.          prompt the user to input the default directory, and will raise a signal
  637.          when the user changes the field value. The other object will be used
  638.          for the input of the other directories, and will include a method to
  639.          handle the signal raised by the first object.
  640.          In this example, the first object is called MasterStringIOOBJ, and it
  641.          will raise a signal whenever the user enters a new directory. The new
  642.          object is declared as follows:
  643.  
  644.          TYPE
  645.          MasterStringIOOBJ = object (StringIOOBJ)
  646.             vLastInput: string;
  647.             {methods}
  648.             constructor Init(X,Y,FieldLen: byte);
  649.             function    ProcessEnter: tAction;                    VIRTUAL;
  650.             function    Select(K:word; X,Y:byte): tAction;        VIRTUAL;
  651.             procedure   RaiseSignal(var TheSig:tSignal);          VIRTUAL;
  652.             procedure   ShutdownSignal(var BaseSig:tSignal);      VIRTUAL;
  653.             function    Suspend:boolean;                          VIRTUAL;
  654.             destructor  Done;                                     VIRTUAL;
  655.          end; {MasterStringIOOBJ}
  656.  
  657.          The new object should only raise a signal when the user has changed the
  658.          value of the field. The new string variable vLastInput is used to
  659.          record the value of the string when the field is selected. The value of
  660.  
  661.  
  662.  
  663.  
  664. 20-14                                                      Extending the Toolkit
  665. --------------------------------------------------------------------------------
  666.  
  667.          vLastInput can then be compared to vInputStr (the edited field value)
  668.          when the user tries to leave the field or presses [KEYCAP]. The method
  669.          Select is therefore declared as follows:
  670.  
  671.          function MasterStringIOOBJ.Select(K:word; X,Y:byte): tAction;
  672.          {}
  673.          begin
  674.             vLastInput := vInputStr;
  675.             Select := StringIOOBJ.Select(K,X,Y);
  676.          end; {MasterStringIOOBJ.Select}
  677.  
  678.          The object needs to raise a signal when the user presses [KEYCAP]. The
  679.          method ProcessEnter is implemented as follows:
  680.  
  681.          function MasterStringIOOBJ.ProcessEnter: tAction;
  682.          {}
  683.          begin
  684.             if vLastInput <> vInputStr then {need to signal}
  685.                ProcessEnter := Signal
  686.             else
  687.                ProcessEnter := none;
  688.          end; {MasterStringIOOBJ.ProcessEnter}
  689.          If the value of the string has changed, SIGNAL is returned, otherwise
  690.          NONE is returned.
  691.  
  692.          The object also needs to raise a signal when the method Suspend is
  693.          called, and the value of the field has changed. Now we are faced with a
  694.          problem, because Suspend cannot directly raise a signal. Suspend
  695.          returns a boolean value to indicate whether the field can be suspended,
  696.          not a tAction value. The trick is to return a boolean value of False,
  697.          indicating that the user may not leave the field, and then stuff the
  698.          keyboard with the keystrokes [KEYCAP] [KEYCAP]. The Toolkit will not
  699.          allow the user to leave the field, the [KEYCAP] key will then be pro-
  700.          cessed, thereby raising a signal via the ProcessEnter method, and
  701.          finally, the [KEYCAP] key will be processed to move the user to the
  702.          next field. The Suspend method is implemented as follows:
  703.          function MasterStringIOOBJ.Suspend:boolean;
  704.          {}
  705.          begin
  706.             if vLastInput <> vInputStr then {need to signal}
  707.             begin
  708.                Suspend := false;
  709.                Key.StuffBuffer(13); {Enter}
  710.                Key.StuffBuffer(9);   {Tab}
  711.             end
  712.             else
  713.                Suspend := StringIOOBJ.Suspend;
  714.          end; {MasterStringIOOBJ.Suspend}
  715.  
  716.  
  717.  
  718.  
  719. Extending Input Field Types                                                20-15
  720. --------------------------------------------------------------------------------
  721.  
  722.          The RaiseSignal method must update the signal variable with the infor-
  723.          mation required by the other fields, i.e. the string representing the
  724.          new directory entered by the user. The RaiseSignal method is
  725.          implemented as follows:
  726.  
  727.          procedure MasterStringIOOBJ.RaiseSignal(var TheSig:tSignal);
  728.          {}
  729.          begin
  730.             with TheSig do
  731.             begin
  732.                ID := SignalNewDirectory;
  733.                MsgType := length(vInputStr);
  734.                MsgPtr := @vInputStr;
  735.             end;
  736.             vLastInput := vInputStr;
  737.          end; {MasterStringIOOBJ.RaiseSignal}
  738.          The signal ID is set to SignalNewDirectory --  a constant assigned the
  739.          value of 1. The MsgType field is set to indicate the length of the
  740.          string, and the variant record MsgPtr is updated to point to the user
  741.          input string. This signal, therefore, provides sufficient data for the
  742.          dependent fields to ascertain the new directory.
  743.  
  744.          In this example, no dynamic data is created for the signal, and so the
  745.          ShutdownSignal method doesn't need to do anything.
  746.  
  747.          Now let's turn our attention to the object which needs to respond to
  748.          the signal raised by MasterStringIOOBJ. In this example, we will name
  749.          the new object SlaveStringIOOBJ, and it will inherit all the properties
  750.          of StringIOOBJ. The only method (in addition to Init and Done) which
  751.          needs to be updated is HandleSignal. This method needs to check the
  752.          value of the Signal and update the value of the field with the new
  753.          directory string. The signal field MsgType stores the length of the new
  754.          string, and the field MsgPtr points to the new string.
  755.  
  756.          The HandleSignal method is implemented as follows:
  757.          procedure SlaveStringIOOBJ.HandleSignal(var BaseSig:tSignal; var NewS-
  758.          ig:tSignal);
  759.          {}
  760.          var temp:string;
  761.          begin
  762.             with BaseSig do
  763.             begin
  764.                if (ID = SignalNewDirectory) then
  765.                begin
  766.                   move(MsgPtr^,Temp,succ(MsgType));
  767.                   if Temp <> vInputStr then
  768.                   begin
  769.                      vInputStr := Temp;
  770.  
  771.  
  772.  
  773.  
  774. 20-16                                                      Extending the Toolkit
  775. --------------------------------------------------------------------------------
  776.  
  777.                      Display(Norm);
  778.                   end;
  779.                end;
  780.             end;
  781.          end; {SlaveStringIOOBJ.HandleSignal}
  782.  
  783.  
  784.  
  785.          To recap, two new field objects have been created. A MasterStringIOOBJ
  786.          field raises a signal when its value is changed, and SlaveStringIOOBJ
  787.          fields change their value accordingly. The on-disk demo file EXT-
  788.          DEM8.PAS includes the full solution to the problem. Figure 20.2 illus-
  789.          trates the output generated by this program.
  790.  
  791.  
  792. Figure 20.2                                                             [SCREEN]
  793. Raising
  794. Signals
  795.  
  796.  
  797.          Review the source code of the DirWinOBJ object in totDIR for another
  798.          example of how fields can communicate with signals.
  799.